#!/usr/bin/python
from threading import Thread, Event, Lock
from time import sleep
from datetime import datetime
import signal
import urllib2
import re
import sys
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from optparse import OptionParser

from daemon import Daemon

class Log:
    def __init__(self, params):
        if 'file' in params:
            self.file = open(params['file'], 'w+')
        else:
            self.file = sys.stdout

    def write(self, data):
        print >>self.file, data

    def close(self):
        self.file.close()

class SiteMonitor(Thread):
    def __init__(self, manager, index,
                 domain, interval, 
                 pages=['/'], content=None):
        super(SiteMonitor, self).__init__()
        self.manager = manager
        self.index = index
        self.domain = domain
        self.interval = interval
        self.pages = pages
        self.content = content
        self.loop_event = Event()

    def run(self):
        page_index = 0
        while not self.loop_event.is_set():
            url = 'http://' + self.domain + self.pages[page_index]
            self.check_url(url)
            page_index += 1
            if page_index >= len(self.pages):
                page_index = 0
            self.loop_event.wait(self.interval)
        self.loop_event.clear()

    def stop(self):
        self.loop_event.set()

    def check_url(self, url):
        t0 = datetime.now()
        status = {'time': t0,
                  'url': url,
                  'response_time': None,
                  'result': None,
                  'content': None}
        try:
            r = urllib2.urlopen(url)
            if 'Content-Length' in r.info():
                l = int(r.info()['Content-Length'])
                c = r.read(l)
            else:
                c = r.read()
            r.close()
            t1 = datetime.now()
            status['result'] = 'OK'
            if self.content:
                fr = re.findall(self.content, c)
                if fr > 0:
                    status['content'] = 'OK'
                else:
                    status['content'] = 'NOK'
        except urllib2.HTTPError, e:
            t1 = datetime.now()
            status['result'] = 'HTTP error'
        except urllib2.URLError, e:
            t1 = datetime.now()
            status['result'] = 'URL error'
        status['response_time'] = t1 - t0
        self.manager.report_status(self.index, status);

class Moniweb(Daemon):
    def run(self):
        print "Moniweb - Simple website monitoring utility"
        parser = OptionParser()
        parser.add_option('-s', '--settings', dest='modulename',
                          help='Python module with settings. Default values is '
                          'settings. Module with settings has to be located in '
                          'the current directory of inside any other directory '
                          'listed in PythonPath.',
                          default='settings')
        (opts, args) = parser.parse_args()
        settings = __import__(opts.modulename)

        # Initializing internals
        if hasattr(settings, 'LOG_ENGINE'):
            (log_module_name, 
             sep, 
             log_class_name) = settings.LOG_ENGINE.rpartition('.')
            log_module = __import__(log_module_name)
            self.log = getattr(log_module, log_class_name)(settings.LOG)
        else:
            self.log = Log(settings.LOG)
        monitors = []
        self.sites = []
        site_index = 0
        for site_settings in settings.SITES:
            site = {'settings': site_settings,
                    'last_status': None}
            sm = SiteMonitor(self, site_index, 
                             site_settings['domain'], 
                             site_settings['interval'])
            if 'content' in site_settings:
                sm.content = site_settings['content']
            if 'pages' in site_settings:
                sm.pages = site_settings['pages']
            monitors.append(sm)
            self.sites.append(site)
            site_index += 1
        self.server = None
        if hasattr(settings, 'WEB_REPORT'):
            self.create_web_report_server(settings.WEB_REPORT)
        self.lock = Lock()

        # Executing monitors
        try:
            if len(monitors) == 0:
                exit('No sites')
            for sm in monitors:
                sm.start()
            if self.server is None:
                signal.pause()
            else:
                self.serve_web_report()
        except KeyboardInterrupt:
            # Shutting down the service
            self.stop_web_report_server()
            for sm in monitors:
                if sm.is_alive():
                    sm.stop()
                    sm.join()
            self.log.close()

    def report_status(self, site_index, status):
        self.lock.acquire()
        site = self.sites[site_index]
        site['last_status'] = status
        self.log.write(status)
        self.lock.release()

    class BaseWebReportHandler(BaseHTTPRequestHandler, object):
        def do_GET(self):
            self.send_response(200)
            self.send_header('Content-Type', 'text/plain')
            self.end_headers()
            self.wfile.write('Moniweb report\n')
            if hasattr(self, 'manager'):
                self.manager.lock.acquire()
                self.wfile.write(self.manager.sites)
                self.manager.lock.release()

    def create_web_report_handler(self):
        WebReportHandler = type('WebReportHandler', 
                                (Moniweb.BaseWebReportHandler,),
                                {'manager': self})
        return WebReportHandler

    def create_web_report_server(self, settings):
        WebReportHandler = self.create_web_report_handler()
        self.server = HTTPServer((settings['host'], settings['port']), 
                                 WebReportHandler)

    def serve_web_report(self):
        self.server.serve_forever()

    def stop_web_report_server(self):
        self.server.server_close()

if __name__ == "__main__":
    daemon = Moniweb('/tmp/moniweb.pid')
    daemon.start(False)
